比特精灵(BitSpirit)扩展协议

1 前言:

从理论上来说,所有的通信协议都是可以向后兼容的。对通信程序而言,为了协议的扩充性及实现的兼容性,通常都会固定每个数据包的格式,同时在实现上会做如下处理:对于未知含义的数据包,只要其组成结构遵从协议包结构规则,则应忽略而不是视为错误。但是,官方版BT(根据继承性还包括大多数基于官方版内核做的改版BT)在实现上禁止了这种兼容性。因此,在目前的测试阶段,BS采用了一种“取巧”的手段来实现扩展。

2 BT协议介绍:

在BT协议里,有一个命令(0x07)是用于标识数据块,该数据包的格式如下:


数据长度 数据含义 数据内容 备注
4 数据量 ? 从命令ID开始算
1 命令ID 0x07  
4 区块索引 ? 从0到区块数 - 1
4 区块起始点 ? 从0到区块大小-1
? 区块数据 ? 长度 = 数据量 - 9

BS 的作者在分析了当前的 BT 协议后,找到了一个在维持当前兼容性前提下实现扩展的策略:构造一个特殊的数据块。

3 BS 扩展协议的基础:

A 在 BS 扩展协议里, BS 构造了一个特殊的数据块,结构如下:

数据长度 数据含义 数据内容 备注
4 数据量 ?

从命令 ID 开始算

1 命令ID 0x07  
4 伪区块索引 0x00000000

固定为 0

4 伪区块起始点 0x00000002

固定为 2

? 区块数据 ?

长度 = 数据量 - 9


B BS 的扩展协议基于如下一种判断:不会存在总大小为 2 个字节的 BT 发布内容。根据 BT 协议, BT 客户端有义务采用 SHA 校验来判断所收到的数据是否正确,同时,所有 BT 客户端在实现上也有义务判断所收到的数据块是否出于自身的请求。令人高兴的是,虽然官方版在收到未知数据包的情况下会关闭连接,但它在收到未知数据块的情况下却只会简单的忽略,这就为在兼容尽可能多的 BT 客户端的前提下实现扩充提供了一种可能。

4 BS 扩展协议的构成:

A 所有 BS 协议都是通过表 2 所描述的结构发送的,也就是说以一个特殊的数据块为载体,具体协议内容封装在表 2 的“区块数据”字段内。

B BS 扩展协议的数据包组成结构:

数据长度 数据含义 数据内容 备注
4 数据量 ?

从命令 ID 开始算

1 命令ID 0x07  
4 伪区块索引 0x00000000

固定为 0

4 伪区块起始点 0x00000002

固定为 2

1 扩展命令ID ?

见表 5

? 协议数据 ?

长度 = 数据量 - 10


C 由于 BT 协议已经定义了数据包的长度,所以 BS 扩展协议不再定义数据包的长度。

D BS 目前已用的命令 ID :

i. 特殊命令 ID :

1. + : 0x2B ,用于向连接对方声明启用某项功能;

2. - : 0x2D ,用于向连接对方声明禁用某项功能;

3. 以上两个命令 ID 是固定的。在实现上,一个连接通过向对方发送: +II 表示支持扩展协议,发送: -II 表示禁用扩展协议;

4. 任何一方在未声明支持扩展协议前如果发送除 +II 或 -II 外的数据包,对方有权关闭连接( BS 的实现是忽略而不是关闭);

5. 通常情况下, +II 或 -II 的命令在握手结束,发送完 BitField 后;

6. 如果对方未声明支持 BS 扩展协议,则本地应默认对方是禁用的;

7. +II 和 -II 的数据包结构:

声明支持BSⅡ扩展协议(下表)

数据长度

数据含义

数据内容 备注
4 数据量 0x0D  
1 命令ID 0x07  
4 伪区块索引 0x00000000

固定为0

4

伪区块起始点

0x00000002

固定为2

1 扩展命令ID 0x2B

ASCII+

3 协议数据

\x49\x49\x00

ASCIIII\0

声明禁用BSⅡ扩展协议(下表)

数据长度 数据含义 数据内容 备注
4 数据量 0x0D  
1 命令ID 0x07  
4 伪区块索引 0x00000000 固定为0
4 伪区块起始点 0x00000002 固定为2
1 扩展命令ID 0x2D ASCII-
3 协议数据 \x49\x49\x00 ASCIIII\0

ii.              可用命令ID

1.        理论上,除了0x2B0x2D以外所有介于0-255之间的数都可以用作命令ID,各命令ID所含协议数据格式由开发者自行定义;

2.        推荐支持的命令ID

A.        ‘+'0x2B,用'+''-‘命令声明自己是否支持GZIP压缩;声明支持GZIP数据压缩(下表)

数据长度 数据含义 数据内容 备注
4 数据量 0x0F  
1 命令ID 0x07  
4 伪区块索引 0x00000000 固定为0
4 伪区块起始点 0x00000002 固定为2
1 扩展命令ID 0x2B ASCII+
5 协议数据 \x47\x5A\x49\x50\x00 ASCIIGZIP\0

B    ‘*'0x2A,压缩过的区块数据,BSII支持在发送数据块前先进行GZIP压缩,以便提高网络传输效率;
数据压缩包格式(下表)


数据长度 数据含义 数据内容 备注
4 数据量 ? 从命令ID开始算
1 命令ID 0x07  
4 伪区块索引 0x00000000 固定为0
4 伪区块起始点 0x00000002 固定为2
1 扩展命令ID 0x2A ASCII*
4 区块索引 ? BT协议类似
4 区块起始点 ? BT协议类似
4 压缩前数据量 ? 不含协议头

C ‘@' : 0x40 ,反向提供本机地址。通常情况下,对于一个连入的连接,本地是不知道连接对方监听的 TCP 端口的,如果因为网络故障导致连接被断开的话,本地将不可能再主动与对方建立连接,通过 '@' 命令,连接发起方可以告知对方本地的监听端口( IP 地址请直接取 TCP 连接的对方 IP )。 (下表)本地反向提供地址

数据长度 数据含义 数据内容 备注
4 数据量 0x10 从命令ID开始算
1 命令ID 0x07  
4 伪区块索引 0x00000000 固定为0
4 伪区块起始点 0x00000002 固定为2
1 扩展命令ID 0x40 ASCII@
4 保留 0 保留,请设为0
2 本地监听端口 本地TCP监听端口

D '%' : 0x25 ,向连接对方请求共享连接信息。在默认 BT 协议里,连接的种子信息只能从 Tracker 服务器获取,而在具体实现上,其实服务器是有不确定性的,很多下载者在很多时候并不一定总能从服务器获取种子信息,这就导致了会有很大的时间上的浪费。 BSII 通过添加“连接共享”这个基于 BS 扩展协议的实现很好的解决了这个问题。连接一方通过向支持扩展协议的对方发送 '%' 命令请求对方共享连接信息。(下表) 向连接对方请求共享连接信息

数据长度 数据含义 数据内容 备注
4 数据量 0x0E 从命令ID开始算
1 命令ID 0x07  
4 伪区块索引 0x00000000 固定为0
4 伪区块起始点 0x00000002 固定为2
1 扩展命令ID 0x25 ASCII%
4 期望连接数 0 11024,参见下表

E ‘&' : 0x26 ,发送连接信息,连接一方在收到对方的 '%' 命令后,向对方发送共享的连接信息。注意不要向对方发送超过对方期望连接数的连接数量( BSII 默认是 256 个连接)。(下表)向连接对方发送连接信息

数据长度 数据含义 数据内容 备注
4 数据量 ? 从命令ID开始算
1 命令ID 0x07  
4 伪区块索引 0x00000000 固定为0
4 伪区块起始点 0x00000002 固定为2
1 扩展命令ID 0x26 ASCII&
1 数据开始标志 10 参考:数据标志
1 数据结束标志 10 参考:数据标志
? 连接信息 ? 经过GZIP压缩

上表中的连接信息是由很多个连接单元 (unit) 组成的,每一个 unit 结构定义如下:

const int PeerSeedSize = 64;

struct tagPeerSeed

{

char None[25]; //25Bytes,not used yet, please set all bytes to 0

int Port; //4Bytes,sorry~~, get it in this way: (ntohl(Port)&0xFFFF)

ulong Address; //4Bytes, in network byte order

char ID[20]; //20Bytes. Peer Id

char Reserved[PeerSeedSize-25-4-4-20]; //set all bytes to 0

};

从目前来看,这个结构有点庸肿,但是由于目前 None 和 Reserved 字段都是 0 ,所以用 GZIP 压缩可以达到很高的压缩比,同时也为将来扩展留下了很好的后路。

: BSII 支持接收未经过 GZIP 压缩的连接信息,但所有 BSII 发送的连接信息都是经过 GZIP 压缩的,这样只占用很少的网络流量。

F 目前 BSII 已经使用的其它命令 ID : '?', ‘=', ‘#', ‘$', ‘(‘, ‘)' ;

G 各个软件作者可以自由使用 0x80-0xFF 的命令 ID ,只要你能确保那是自己定义的数据包 J ,但对于 0x00-0x7F 的命令 ID ,请尽可能的先跟其他软件作者联系 ( 比如 BS) 。

附注 1 :关于数据压缩 BS 采用基于 zlib 的 GZIP 压缩技术,当把内存数据块进行压缩时,压缩后的数据块包含标准的 GZIP 数据头,但不包含 GZIP 文件的结束标志 (zlib 只提供了基于文件的 GZIP 实现,没有提供基于内存的 GZIP 压缩实现, BS 的作者根据 zlib 自行实现了一个模块用于对内存块进行压缩,如果你需要这方面的资料可以跟 BS 的作者联系 ) 。

附注 2 :关于在 '%' 命令里提到的数据标志:因为 BT 协议定义了数据包最大不能超过 128KB ,所以 BS 的扩展协议的数据包一般不超过 64KB ,这样在传送大数据块时力有不足,所以 BS 通过数据标志来组合大块数据,在表 10 中有一个数据开始标志和数据结束标志,以下是其含义:

1 数据开始标志: 1 表示这块数据是一大块数据的开始, 0 则表示本块数据是前一块未完数据的继续;

2 数据结束标志: 1 表示这块数据是一大块数据的结束, 0 则表示本块数据是前一块未完数据的继续;

3 如果前一个数据块未包含结束标志,而本数据块包含了开始标志,则表示以前的数据已经作废,对方必须重新表示当前数据;

4 如果所有数据可以通过一个数据块发完,那么可以将开始和结束标志都置为 1 ;

 

5 以上协议定义只是 BS 作者对 BT 扩展所做的一些尝试。 BS 作者很愿意就扩展应采用的方式与具体扩展内容的实现与各 BT 软件作者进行商讨,共同就 BT 扩展而努力。另外, BS 目前扩展所用的手段是很取巧的,我们本身不推荐采用这种方式扩展,所以希望能跟各 BT 软件作者共同商讨一个合适的扩展方式。但是也希望大家能把更多的注意力放在扩展的内容上,不用流于表面,在扩展方式上浪费太多精力。

6 这份文档只简要叙述了 BS 扩展协议的实现方式及一些基本内容, P2P 聊天,种子市场等基于该实现方式的后续扩展尚未添加。

 

作者:比特精灵
联系: webmaster@lanspirit.net
初稿: 2004-02-10